package com.tobestronger.shouxiespringframework.beans.factory.support;


import com.tobestronger.shouxiespringframework.beans.BeansException;
import com.tobestronger.shouxiespringframework.beans.factory.DisposableBean;
import com.tobestronger.shouxiespringframework.beans.factory.ObjectFactory;
import com.tobestronger.shouxiespringframework.beans.factory.config.SingletonBeanRegistry;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 通用的单例Bean注册表实现
 */
public class DefaultSingletonBeanRegistry implements SingletonBeanRegistry {

    /**
     * Internal marker for a null singleton object:
     * used as marker value for concurrent Maps (which don't support null values).
     */
    protected static final Object NULL_OBJECT = new Object();

    /**
     * 一级缓存，存放已经完成的 成品对象
     * Cache of singleton objects: bean name --> bean instance
     */
    private Map<String, Object> singletonObjects = new ConcurrentHashMap<>();


    /**
     * 二级缓存，用于提前暴漏对象，存放没有完全实例化的 半成品对象
     * Cache of early singleton objects: bean name --> bean instance
     */
    protected final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>();

    /**
     * 三级缓存，存放 对象工厂,只有代理对象才会放到三级缓存中
     * Cache of singleton factories: bean name --> ObjectFactory
     */
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>();

    private final Map<String, DisposableBean> disposableBeans = new LinkedHashMap<>();

    @Override
    public Object getSingleton(String beanName) {

        //先从一级缓存找; 如果找到直接返回
        Object singletonObject = singletonObjects.get(beanName);

        if (null == singletonObject) {

            //一级没找到,尝试从二级缓存找; 如果找到直接返回
            singletonObject = earlySingletonObjects.get(beanName);

            if (null == singletonObject) {

                //二级缓存中也没有找到, 那就需要从三级缓存中获取到对应的对象工厂
                ObjectFactory<?> singletonFactory = singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    //通过对象工厂创建实际对象
                    singletonObject = singletonFactory.getObject();
                    //此时对象只是创建出来了,还没有设置相关属性,所以把它放到二级缓存中去
                    earlySingletonObjects.put(beanName, singletonObject);
                    //至此,对象工厂使命完成,从三级缓存中移除
                    singletonFactories.remove(beanName);
                }

            }

        }

        return singletonObject;
    }

    @Override
    public void registerSingleton(String beanName, Object singletonObject) {
        singletonObjects.put(beanName, singletonObject);
        //注册到一级缓存时,说明bean已经处理完,则需要从二级缓存中移除
        earlySingletonObjects.remove(beanName);
        singletonFactories.remove(beanName); //顺带从三级缓存中移除. 其实bean只会在三级中的某一级,在二级那就没在三级,这里按照作者的写法顺带也删一下好了
    }


    /**
     * 将ObjectFactory放入三级缓存
     *
     * @param beanName
     * @param singletonFactory
     */
    protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory){
        if (!this.singletonObjects.containsKey(beanName)) {
            this.singletonFactories.put(beanName, singletonFactory);
            this.earlySingletonObjects.remove(beanName);
        }
    }

    public void registerDisposableBean(String beanName, DisposableBean bean) {
        disposableBeans.put(beanName, bean);
    }

    @Override
    public void destroySingletons() {
        Set<String> keySet = this.disposableBeans.keySet();
        Object[] disposableBeanNames = keySet.toArray();

        for (int i = disposableBeanNames.length - 1; i >= 0; i--) {
            Object beanName = disposableBeanNames[i];
            DisposableBean disposableBean = disposableBeans.remove(beanName);
            try {
                disposableBean.destroy();
            } catch (Exception e) {
                throw new BeansException("Destroy method on bean with name '" + beanName + "' threw an exception", e);
            }
        }
    }

}
